/******************************************************************
 * This program was written as part of
 *   Assignment 4
 *   Computer Graphics                                             
 *   Semester 1, 2013
 *   School of Computer Science,
 *   The University of Adelaide
 *                                                                          
 * I declare that all material in this assessment is my own work except where
 * there is clear acknowledgement and reference to the work of others. I have
 * read the University Policy Statement on Plagiarism, Collusion and Related
 * Forms of Cheating:  

 * http://www.adelaide.edu.au/policies/?230

 * I give permission for my assessment work, including my subversion
 * repository, to be reproduced and submitted to academic staff for the
 * purposes of assessment and to be copied, submitted and retained in a form
 * suitable for electronic checking of plagiarism.  
                            
 * Authors:
 *  Terry Moschou    a1208955
 *  Salih Al-Rashid  a1210108
 *  Ben McAleer      a1212203
*******************************************************************/

#include "Player.hpp"

#include "Model.hpp"

#include "Context.hpp"
#include "Scene.hpp"
#include "Terrain.hpp"
#include "Transform.hpp"

#define DELTA_LEG_ANGLE 5.0
#define DELTA_LEG_ANGLE_MAX 45.0
#define DELTA_ARM_ANGLE 4.0
#define DELTA_ARM_ANGLE_MAX 36.0
#define DELTA_MOVE 0.2
#define DELTA_ROT 7.2
#define PI 3.14
#define GRAVY 0.05

  const mat4 head_mat = glm::translate(mat4(1.0f), vec3(0.0f, 2.5f, 0.0f));
  const mat4 leftLeg_mat = glm::translate(mat4(1.0f), vec3(0.312f, 1.25f, 0.0f));
  const mat4 rightLeg_mat = glm::translate(mat4(1.0f), vec3(-0.312f, 1.25f, 0.0f));
  const mat4 leftArm_mat = glm::translate(mat4(1.0f), vec3(0.5f, 2.125f, 0.0f));
  const mat4 rightArm_mat = glm::rotate(glm::translate(mat4(1.0f), vec3(-0.5f, 2.125f, 0.0f)), 180.0f,vec3(0.0f, 1.0f, 0.0f));
  

Player::Player(Object * parent /* = 0 */,
               const glm::mat4 & playerModelMatrix /* = glm::mat4(1.0f) */)
  : Object(parent, playerModelMatrix, 0, "Player")
{
  Model * model = new Model("resources/models/player_body.obj");
  m_body = new Object(this, mat4(1.0f), model,"Player Body");
  model = new Model("resources/models/player_head.obj");
  m_head = new Object(this, head_mat, model, "Player Head");
  
  model = new Model("resources/models/player_leg.obj");
  m_leftLeg = new Object(this, leftLeg_mat, model, "Player Left Leg");
  
  m_rightLeg = new Object(this, rightLeg_mat, model, "Player Right Leg");
  
  model = new Model("resources/models/player_arm.obj");
  m_leftArm = new Object(this, leftArm_mat, model, "Player Left Wacking Stick");
  
  m_rightArm = new Object(this, rightArm_mat, model, "Player Other Left Wacking Stick");
  
  m_grounded = true;
  m_speed = 0.0f;
  m_segway = false;
  m_equipped = false;
  
}

/**
bool Player::canMoveTo(vec2 pos, vec2 dis)
{
  Object * obj = static_cast<Object>(g_context.scene);
   
  while (obj != 0) {
    
  }
  
}
*/
void Player::update() {
  g_context.m_scene->m_terrain->goforit = true;
  
  UIState &state = UserInterface::g_state;
  
  mat4 ident(1.0f);
  
  // get new player position
  
  int dir_move = 0;
  int dir_angle = 0;
  
  m_headRotRight = -state.m_cursor.x*10.0f;
  m_headRotDown = state.m_cursor.y*10.0f;
  
  if(state.m_w) dir_move = 1;
  if(state.m_s) dir_move = -1;
  if(state.m_d) dir_angle = -1;
  if(state.m_a) dir_angle = 1;
  if(state.m_space && m_grounded)
  { m_speed = 0.5f; m_grounded = false; } //I can fly
  
  Object * e = g_context.near(m_pos,"Equipment");
  bool segnear = (!m_segway && !m_equipped && g_context.near(m_pos,"Segway Model"));
  
  //fairy time!
  if(!m_equipped && !m_segway) {
  
    if(segnear) {
      g_context.m_scene->m_navi->m_pos = g_context.getSegway()->m_pos+vec3(0,5,0);
      g_context.m_scene->m_navi->stoppit = true;
    } else if(e) {
      g_context.m_scene->m_navi->m_pos = ((Equipment*)e)->m_pos+vec3(0,4.5,0);
      g_context.m_scene->m_navi->stoppit = true;
    } else {
      g_context.m_scene->m_navi->m_pos = m_pos+vec3(2,4,2);
      g_context.m_scene->m_navi->stoppit = false;
    }
    
  } else {
    g_context.m_scene->m_navi->m_pos = m_pos+vec3(2,4,-2);
    g_context.m_scene->m_navi->stoppit = false;
  }
  
  //E key (Jump on Segway or grab weapon)
  if(state.m_e) {
    if(m_swing == 0)
      m_swing = 1; //ATTACK!!!!
    if(segnear) {
      g_context.getSegway()->m_mounted = true;
      g_context.getSegway()->attachChild(this); //Let's do this
      m_segway = true; //Leroy Jenkins!
      setModelMatrix(ident);
      mat4 leftArmTransform = glm::rotate(leftArm_mat, -90.0f, vec3(1.0f, 0.0f, 0.0f));
      mat4 rightArmTransform = glm::rotate(rightArm_mat, 90.0f, vec3(1.0f, 0.0f, 0.0f));
      m_leftArm->setModelMatrix(leftArmTransform);
      m_rightArm->setModelMatrix(rightArmTransform);
      m_rightLeg->setModelMatrix(rightLeg_mat);
      m_leftLeg->setModelMatrix(leftLeg_mat);
      mat4 headMatrix = glm::rotate(head_mat, m_headRotRight, vec3(0.0f, 1.0f, 0.0f));
      headMatrix = glm::rotate(headMatrix, m_headRotDown, vec3(1.0f, 0.0f, 0.0f));
      m_head->setModelMatrix(headMatrix);
    } else if(!m_equipped && !m_segway) {
      if(e) {
        m_equipment = (Equipment*)e; //This ain't just your typical object
        m_equipped = true;
        m_equipment->m_equipped = true;
        m_rightArm->attachChild(m_equipment);
        m_equipment->update();
        m_swing = 0; //we can't be swing anything around willy nilly
      }
    }
  } else if(state.m_q) { //Don't be a quitter, just drop it
    if(m_segway) {
      g_context.m_scene->attachChild(this); //Back home
      m_segway = false;
      g_context.getSegway()->m_mounted = false;
      g_context.getSegway()->m_grounded = false;
      g_context.getSegway()->m_hspeed += 0.1f;
      g_context.getSegway()->m_vspeed = -GRAVY;
      m_pos = g_context.getSegway()->m_pos;
      m_rot = g_context.getSegway()->m_rot;
      m_grounded = false;
      m_pos.y += 0.5f;
      m_speed = 0.5f;
    } else if (m_equipped) {
      m_equipped = false;
      m_equipment->m_pos = m_pos;
      m_equipment->m_rot = m_rot;
      m_equipment->m_equipped = false;
      m_equipment->m_pos += vec3(sin(m_rot*PI/180-PI/2),0.0f,cos(m_rot*PI/180-PI/2));
      g_context.m_scene->attachChild(m_equipment); //I'll leave you here
      m_equipment->update();
      m_equipment = 0;
    }
  }
  
  if(m_equipped) {
    
    if(m_swing) {
      m_swing++;
      if(m_swing > 49)
        m_swing = 0;
    } else if(state.m_q) {
      m_swing = 1;
    }
  }
  
  if(!m_segway) {
    
    m_rot += DELTA_ROT*dir_angle;
    if (m_rot >= 360.0f)
      m_rot -= 360.0f;
    if (m_rot < 0.0f)
      m_rot += 360.0f;
    
    vec3 posPrev = m_pos;
    float armRotPrev = m_armRot;
    float legRotPrev = m_legRot;
  
    m_pos.x += DELTA_MOVE*dir_move*sin(m_rot*PI/180);
    m_pos.z += DELTA_MOVE*dir_move*cos(m_rot*PI/180);
    
    float ground = g_context.m_scene->m_terrain->getHeight(m_pos.x, m_pos.z);

    //Ouch?
    bool ouch = false;
    for(float ox = -0.8; ox < 0.9; ox = ox + 0.8)
      for(float oi = 0.0; oi < 3.1; oi = oi + 1.5)
        for(float oz = -0.8; oz < 0.9; oz = oz + 0.8)
          if(!g_context.boundcheck(m_pos+vec3(ox,oi,oz)))
            ouch = true;

    if(ouch) {
      m_pos = posPrev;
      dir_move = 0;
      ground = g_context.m_scene->m_terrain->getHeight(m_pos.x, m_pos.z);
      if(!g_context.boundcheck(m_pos+vec3(0.0,3.0,0.0))) {
        m_speed = 0.0f;
        m_grounded = false;
      }
    }
    
    //arm and leg movements
    if(dir_move != 0) {
      //Player continues to move
      if(m_moving) {
        m_legRot += DELTA_LEG_ANGLE*m_rightLegFirst*dir_move;
        m_armRot += DELTA_ARM_ANGLE*m_rightLegFirst*dir_move;
        
        //arm and leg upperbound
        if(m_legRot >= DELTA_LEG_ANGLE_MAX) {
          m_rightLegFirst = -m_rightLegFirst;
          m_legRot = DELTA_LEG_ANGLE_MAX;
          m_armRot = DELTA_ARM_ANGLE_MAX;
        }
        
        //arm and leg lowerbound
        if(m_legRot <= -DELTA_LEG_ANGLE_MAX) {
          m_rightLegFirst = -m_rightLegFirst;
          m_legRot = -DELTA_LEG_ANGLE_MAX;
          m_armRot = -DELTA_ARM_ANGLE_MAX;
        }
      } else {
        //Player starts moving
        m_moving = true;
        m_legRot = DELTA_LEG_ANGLE*dir_move;
        m_armRot = DELTA_ARM_ANGLE*dir_move;
        m_rightLegFirst = 1;
      }
    } else if(m_moving) {
      //player stops
      m_moving = false;
      m_legRot = 0.0f;
      m_armRot = 0.0f;
    }
    

    bool prev = false;
    
    if(m_grounded)
      m_pos.y = ground;
    else {
      m_pos.y += m_speed;
      m_speed -= GRAVY;
      if(m_pos.y <= ground) {
        m_grounded = true;
        m_army = !m_army;
        m_speed = 0;
        m_pos.y = ground;
        if(dir_move == 0) {
          //player stops
            m_moving = false;
            m_legRot = 0.0f;
            m_armRot = 0.0f;
        }
      } else {
        prev = true;
        if(m_army) {
        m_armRot = 120.0;
        m_legRot = 60.0;
        } else {
        m_armRot = -120.0;
        m_legRot = -60.0;
        }
      }
    }

    //Arm transformation
    mat4 leftArmTransform = glm::rotate(leftArm_mat, m_armRot, vec3(1.0f, 0.0f, 0.0f));
    mat4 rightArmTransform = glm::rotate(rightArm_mat, m_armRot, vec3(1.0f, 0.0f, 0.0f));
    m_leftArm->setModelMatrix(leftArmTransform);
    if(m_equipped) {
      float s;
      rightArmTransform = glm::rotate(rightArm_mat, 90.0f, vec3(1.0f, 0.0f, 0.0f));
      
      if(m_swing > 1 && m_swing < 11) {
        s = 5.0f*(m_swing-1);
        rightArmTransform = glm::rotate(rightArmTransform, 2*s, vec3(1.0f, 0.0f, 0.0f));
        rightArmTransform = glm::rotate(rightArmTransform, -s, vec3(0.0f, 1.0f, 0.0f));
      }

      if(m_swing >= 11) {
        rightArmTransform = glm::rotate(rightArm_mat, 180.0f, vec3(1.0f, 0.0f, 0.0f));
        rightArmTransform = glm::rotate(rightArmTransform, -45.0f, vec3(0.0f, 1.0f, 0.0f));
      }

      if(m_swing > 20 && m_swing < 35) {
        s = -5.0f*(m_swing-20);
        rightArmTransform = glm::rotate(rightArm_mat, 180.0f+s*2, vec3(1.0f, 0.0f, 0.0f));
      }
      if(m_swing > 20 && m_swing < 30)
        rightArmTransform = glm::rotate(rightArmTransform, -45.0f-s, vec3(0.0f, 1.0f, 0.0f));
      if(m_swing > 29 && m_swing < 35) {
        s = 10.0f*(m_swing-29);
        rightArmTransform = glm::rotate(rightArmTransform, s, vec3(0.0f, 1.0f, 0.0f));
      }
      
      if(m_swing >= 35) {
        rightArmTransform = glm::rotate(rightArm_mat, 45.0f, vec3(1.0f, 0.0f, 0.0f));
        rightArmTransform = glm::rotate(rightArmTransform, 45.0f, vec3(0.0f, 1.0f, 0.0f));
      }
      if(m_swing > 40 && m_swing < 50) {
        s = 5.0f*(m_swing-40);
        rightArmTransform = glm::rotate(rightArm_mat, 45.0f+s, vec3(1.0f, 0.0f, 0.0f));
        rightArmTransform = glm::rotate(rightArmTransform, 45.0f-s, vec3(0.0f, 1.0f, 0.0f));
      }
      
    }
    m_rightArm->setModelMatrix(rightArmTransform);
    
    //Leg transformation
    mat4 rightLegTransform = glm::rotate(rightLeg_mat, m_legRot, vec3(1.0f, 0.0f, 0.0f));
    m_rightLeg->setModelMatrix(rightLegTransform);

    mat4 leftLegTransform = glm::rotate(leftLeg_mat, m_legRot, vec3(-1.0f, 0.0f, 0.0f));
    m_leftLeg->setModelMatrix(leftLegTransform);
    
    //Head matrix
    mat4 headMatrix = glm::rotate(head_mat, m_headRotRight, vec3(0.0f, 1.0f, 0.0f));
    headMatrix = glm::rotate(headMatrix, m_headRotDown, vec3(1.0f, 0.0f, 0.0f));
    m_head->setModelMatrix(headMatrix);
    
    //Person transformation
    mat4 playerTransform = glm::rotate(glm::translate(ident, m_pos), m_rot, vec3(0.0f, 1.0f, 0.0f));
    setModelMatrix(playerTransform);
    
    if(prev) {
      m_armRot = armRotPrev;
      m_legRot = legRotPrev;
    }
    
  }
  if(m_swing == 32 && m_equipment->m_type == SWORD)
    if(!g_context.boundcheck(m_pos+vec3(0.0,0.0,-3.0)))
      if(g_context.m_scene->m_terrain->m_collision->getName().find("Secret Door") != std::string::npos) {
        g_context.m_scene->m_terrain->m_collision->setModelMatrix(mat4(1.0f));
      }
  
  Object::update();
  g_context.m_scene->m_terrain->goforit = false;
}

glm::mat4 Player::header()
{
  return m_head->getWorldModelMatrix();  
}
